//
//  TMController.m
//  Textmaster
//
//  Created by Anshu Chimala on 1/1/10.
//  Copyright 2010 MediaLocal LLC. All rights reserved.
//

#import "TMController.h"
#import "TMMacro.h"
#import "TextmasterAppDelegate.h"
#import "TMPreferences.h"
#import <BWToolkitFramework/BWToolkitFramework.h>

@implementation TMController
@dynamic showActivityIndicator;
//@dynamic userCanEditSampleText;
@synthesize shouldPasteAfterwards = _shouldPasteAfterwards;
@synthesize tableView = _tableView;
@synthesize titleText = _titleText, authorText = _authorText;
@synthesize descriptionText = _descriptionText, beforeText = _beforeText, afterText = _afterText;
@synthesize searchText = _searchText;
@synthesize macros = _macros;

// I suppose it's a fair assumption that no two macros have the same name...?
// Also oh my goodness this is a really bad implementation there's almosy
// certainly a better way to do this why am I not using a dictionary honestly.
- (NSString *)fileNameForMacroTitle:(NSString *)title {
	NSString *pluginPath = [@"~/Library/Application Support/Textmaster/PlugIns" stringByExpandingTildeInPath];
	if(![[NSFileManager defaultManager] fileExistsAtPath:pluginPath])
		[[NSFileManager defaultManager] createDirectoryAtPath:pluginPath
								  withIntermediateDirectories:YES
												   attributes:nil
														error:nil];
	
	NSArray *all = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pluginPath error:nil];
	
	id macro = nil;
	NSBundle *macroBundle = nil;
	
	for(NSString *path in all) {
		if(![[path pathExtension] isEqualToString:@"txmacro"])
			continue;
		
		macroBundle = [NSBundle bundleWithPath:[pluginPath stringByAppendingPathComponent:path]];
		[macroBundle load];
		
		Class prinClass = [macroBundle principalClass];
		
		if(![prinClass conformsToProtocol:@protocol(TMMacro)])
			continue;
		
		macro = [[prinClass alloc] init];
		if([[macro title] isEqualToString:title])
			return path;
	}
	
	return nil;
}

- (void)loadMacros {
	self.macros = [NSMutableArray array];
	
//	NSString *appPluginPath = [[NSBundle mainBundle] builtInPlugInsPath];
//	if([[NSFileManager defaultManager] fileExistsAtPath:pluginPath]) {
//		// Install these built-in macros
//		
//		NSString *pluginPath = [@"~/Library/Application Support/Textmaster/PlugIns" stringByExpandingTildeInPath];
//		if(![[NSFileManager defaultManager] fileExistsAtPath:pluginPath])
//			[[NSFileManager defaultManager] createDirectoryAtPath:pluginPath
//									  withIntermediateDirectories:YES
//													   attributes:nil
//															error:nil];
//		
//		NSArray *all = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:appPluginPath error:nil];
//		
//		id macro = nil;
//		NSBundle *macroBundle = nil;
//		
//		for(NSString *path in all) {
//			if(![[path pathExtension] isEqualToString:@"txmacro"])
//				continue;
//			
//			[[NSFileManager defaultManager] copyItemAtPath:path toPath:pluginPath error:nil]; //TODO: FINISH THIS
//			
//			macroBundle = [NSBundle bundleWithPath:[pluginPath stringByAppendingPathComponent:path]];
//			[macroBundle load];
//			
//			Class prinClass = [macroBundle principalClass];
//			
//			if(![prinClass conformsToProtocol:@protocol(TMMacro)])
//				continue;
//			
//			macro = [[prinClass alloc] init];
//			[macro setDelegate:self];
//			[_macros addObject:macro];
//			[macro release], macro = nil;
//			//		[macroBundle unload];
//			macroBundle = nil;
//		}		
//	}
	
	NSString *pluginPath = [@"~/Library/Application Support/Textmaster/PlugIns" stringByExpandingTildeInPath];
	if(![[NSFileManager defaultManager] fileExistsAtPath:pluginPath])
		[[NSFileManager defaultManager] createDirectoryAtPath:pluginPath
								  withIntermediateDirectories:YES
												   attributes:nil
														error:nil];
	
	NSArray *all = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pluginPath error:nil];

	id macro = nil;
	NSBundle *macroBundle = nil;
	
	for(NSString *path in all) {
		if(![[path pathExtension] isEqualToString:@"txmacro"])
			continue;
		
		macroBundle = [NSBundle bundleWithPath:[pluginPath stringByAppendingPathComponent:path]];
		[macroBundle load];
		
		Class prinClass = [macroBundle principalClass];
		
		if(![prinClass conformsToProtocol:@protocol(TMMacro)])
			continue;
		
		macro = [[prinClass alloc] init];
		[macro setDelegate:self];
		[_macros addObject:macro];
		[macro release], macro = nil;
//		[macroBundle unload];
		macroBundle = nil;
	}
}

- (void)awakeFromNib {
	if(![[NSUserDefaults standardUserDefaults] boolForKey:@"hasRunOnce"]) {
		[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"hasRunOnce"];
		[[_welcomeScreen animator] setAlphaValue:0];
		[_welcomeScreen makeKeyAndOrderFront:nil];
	}
	else
		[NSApp hide:nil]; // Start off hidden until the user hits the key.
	
	[self loadMacros];
	_filteredMacros = nil;
	
	// Weird, but I'll take it.
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(doSearch:) name:@"becameVisible" object:nil];
	
	NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
    [attributes setObject:[_beforeText font] forKey:NSFontAttributeName];
	[attributes setObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName];
	[_beforeText setTypingAttributes:attributes];
	[_afterText setTypingAttributes:attributes];
	[attributes release];
	
	[self setShowActivityIndicator:NO];
//	[self setUserCanEditSampleText:YES]; // dropping this feature for now.
	_shouldPasteAfterwards = YES;
	
	[_tableView reloadData];
	[self tableViewSelectionDidChange:nil];
	
	TMWindow *window = [(TextmasterAppDelegate *)[NSApp delegate] window];
	[window setFrameAutosaveName:@"_window_autosave"];
	[_catalogScreen setFrameAutosaveName:@"_catalogScreen_autosave"];

	// Set them as faded out initially, so that they fade in when they're made key.
	[[_preferencesScreen animator] setAlphaValue:0];
	[[window animator] setAlphaValue:0];
}

- (float)titleBarHeight {
    NSRect frame = NSMakeRect (0, 0, 100, 100);
    NSRect contentRect;
    contentRect = [NSWindow contentRectForFrameRect: frame
										  styleMask: NSTitledWindowMask];
    return (frame.size.height - contentRect.size.height);	
}

- (void)controlTextDidChange:(NSNotification *)obj {
	[self doSearch:nil];
}

- (void)controlTextDidEndEditing:(NSNotification *)obj {
	if([[_searchText stringValue] length])
		[self executeMacro:nil];
}

- (void)doSearch:(id)sender {
	NSString *query = [_searchText stringValue];
	NSMutableArray *filteredMacros = [[NSMutableArray alloc] init];
	
	if(![query length]) {
		[filteredMacros release], filteredMacros = nil;
		[_filteredMacros release], _filteredMacros = nil;
		[_tableView reloadData];
		[_tableView deselectAll:nil];
		return;
	}
	
	for(id<TMMacro> macro in _macros) {
		if([[macro title] rangeOfString:query options:NSCaseInsensitiveSearch].location != NSNotFound)
			[filteredMacros addObject:macro];
		else if([TMPreferences filterByDescription]) {
			if([[macro description] rangeOfString:query options:NSCaseInsensitiveSearch].location != NSNotFound)
			[filteredMacros addObject:macro];
		}
	}
	
//	if([filteredMacros count])
		_filteredMacros = [filteredMacros copy];
	[filteredMacros release], filteredMacros = nil;
	
	[_tableView reloadData];
	[_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
	[self tableViewSelectionDidChange:nil];
}

- (IBAction)showAboutPanel:(id)sender {
	TMWindow *window = [(TextmasterAppDelegate *)[NSApp delegate] window];
	[NSApp beginSheet:_aboutScreen modalForWindow:window modalDelegate:nil didEndSelector:nil contextInfo:nil];
}

- (IBAction)dismissAboutPanel:(id)sender {
	[NSApp endSheet:_aboutScreen];
	[_aboutScreen orderOut:nil];
}

- (IBAction)showPreferences:(id)sender {
	[_preferencesScreen makeKeyAndOrderFront:nil];
}

- (IBAction)dismissPreferences:(id)sender {
	[_preferencesScreen orderOut:nil];
	
	TMWindow *window = [(TextmasterAppDelegate *)[NSApp delegate] window];
	[window performSelector:@selector(makeKeyAndOrderFront:) withObject:nil afterDelay:.15];
}

- (IBAction)showCatalog:(id)sender {
	TMWindow *window = [(TextmasterAppDelegate *)[NSApp delegate] window];
	[NSApp beginSheet:_catalogScreen modalForWindow:window modalDelegate:nil didEndSelector:nil contextInfo:nil];
}

- (IBAction)dismissCatalog:(id)sender {
	[NSApp endSheet:_catalogScreen];
	[_catalogScreen orderOut:nil];
	
	[self loadMacros];
	[self doSearch:nil];
	
	[[NSNotificationCenter defaultCenter] postNotificationName:@"resetCatalog" object:nil];
}

- (IBAction)dismissWelcomePanel:(id)sender {
	[_welcomeScreen orderOut:nil];
	
	// Wait for _welcomeScreen to fade out
	// (Extra time in case there's another app that wants to take focus in between. I know, it's weird,
	// deal with it.)
	[(TextmasterAppDelegate *)[NSApp delegate] performSelector:@selector(toggleHidden) withObject:nil afterDelay:.5];
}

- (IBAction)dismissWelcomePanelAndShowCatalog:(id)sender {
	[self dismissWelcomePanel:nil];
	
	// Make sure the window has ample time to fade in.
	[self performSelector:@selector(showCatalog:) withObject:nil afterDelay:.8];
}

- (IBAction)addMacroFromFile:(id)sender {
	TMWindow *window = [(TextmasterAppDelegate *)[NSApp delegate] window];
	NSOpenPanel *panel = [[NSOpenPanel alloc] init];
	[panel setAllowsMultipleSelection:YES];
	[panel setCanChooseDirectories:NO];
	[panel setRequiredFileType:@"txmacro"];
	[panel beginSheetForDirectory:nil
							 file:nil
							types:[NSArray arrayWithObject:@"txmacro"]
				   modalForWindow:window
					modalDelegate:self
				   didEndSelector:@selector(filePanelDidEnd:returnCode:contextInfo:)
					  contextInfo:nil];
	[panel release];
}

- (void)installMacroAtPath:(NSString *)file {
	NSString *name = [file lastPathComponent];
	NSError *err = nil;
	NSFileManager *manager = [NSFileManager defaultManager];
	[manager copyItemAtPath:file toPath:[[@"~/Library/Application Support/Textmaster/PlugIns/" stringByExpandingTildeInPath] stringByAppendingPathComponent:name] error:&err];
	if(err && [err code] != 0) {
		NSAlert *alert = [NSAlert alertWithMessageText:@"Error" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:[err localizedDescription]];
		[alert setAlertStyle:NSCriticalAlertStyle];
		[alert runModal];
		return;
	}
	[self loadMacros];
	[self doSearch:nil];	// Reloads the table view and the filtering
}

- (void)filePanelDidEnd:(NSOpenPanel*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo {
	if (returnCode != NSOKButton)
		return;
	
	NSArray *filesToOpen = [sheet filenames];
	for(NSString *file in filesToOpen)
		[self installMacroAtPath:file];
}

- (IBAction)deleteMacro:(id)sender {
	int row = [_tableView selectedRow];
	if(row<0)
		return;

	NSObject<TMMacro> *macro;
	if(_filteredMacros)
		macro = [_filteredMacros objectAtIndex:row];
	else
		macro = [_macros objectAtIndex:row];
	NSString *title = [macro title];
	TMWindow *window = [(TextmasterAppDelegate *)[NSApp delegate] window];
	NSAlert *alert = [NSAlert alertWithMessageText:@"Delete Macro?" defaultButton:@"Yes" alternateButton:@"No" otherButton:nil informativeTextWithFormat:[NSString stringWithFormat:@"Are you sure you would like to delete the %@ macro? You will have to re-download it if you should need it again.", title]];
	[alert beginSheetModalForWindow:window modalDelegate:self didEndSelector:@selector(confirmDeleteAlertDidEnd:returnCode:contextInfo:) contextInfo:nil];
}

-(void)confirmDeleteAlertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo {
	// Yes
	if(returnCode == 1)  {
		NSFileManager *manager = [NSFileManager defaultManager];
		NSError *err = nil;
		int row = [_tableView selectedRow];
		NSObject<TMMacro> *macro;
		if(_filteredMacros)
			macro = [_filteredMacros objectAtIndex:row];
		else
			macro = [_macros objectAtIndex:row];
		NSString *title = [macro title];
		
		[manager removeItemAtPath:[[@"~/Library/Application Support/Textmaster/PlugIns/" stringByExpandingTildeInPath] stringByAppendingPathComponent:([self fileNameForMacroTitle:title] ?: @"")] error:&err];
		if(err && [err code] != 0) {
			NSAlert *alert = [NSAlert alertWithMessageText:@"Error" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:[err localizedDescription]];
			[alert setAlertStyle:NSCriticalAlertStyle];
			[alert runModal];
		}
		[self loadMacros];
		[self doSearch:nil];
	}
}

- (void)dealloc {
	self.macros = nil;
	[_filteredMacros release], _filteredMacros = nil;
	
	[super dealloc];
}

#pragma mark -
#pragma mark TMMacroDelegate

- (void)setShowActivityIndicator:(BOOL)b {
	_showActivityIndicator = b;
	if(_showActivityIndicator) {
		int row = [_tableView selectedRow];
		if(row<0)
			return;
		NSObject<TMMacro> *macro;
		if(_filteredMacros)
			macro = [_filteredMacros objectAtIndex:row];
		else
			macro = [_macros objectAtIndex:row];
		
		if([macro respondsToSelector:@selector(progressMessage)])
			[_progressLabel setStringValue:[macro progressMessage]];
		else
			[_progressLabel setStringValue:@"Working..."];
		
		TMWindow *window = [(TextmasterAppDelegate *)[NSApp delegate] window];
		[_progressWheel setIndeterminate:YES];
		[_progressWheel startAnimation:nil];
		
		if([window attachedSheet] != _progressScreen)
			[NSApp beginSheet:_progressScreen modalForWindow:window modalDelegate:nil didEndSelector:nil contextInfo:nil];
	}
	else {
		[NSApp endSheet:_progressScreen];
		[_progressScreen orderOut:nil];
	}
}

- (BOOL)showActivityIndicator {
	return _showActivityIndicator;
}

- (void)setUserCanEditSampleText:(BOOL)b {
	_userCanEditSampleText = b;
	[_beforeText setEditable:NO];//_userCanEditSampleText];
	// This is kind of useless so I'll worry about it later.
}

- (BOOL)userCanEditSampleText {
	return _userCanEditSampleText;
}

- (void)macro:(id<TMMacro>)macro didProcessTextWithResult:(NSString *)result {
	[self setShowActivityIndicator:NO];
	
	if(result != nil) {
		NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
		[pasteBoard declareTypes:[NSArray arrayWithObjects:NSStringPboardType, nil] owner:nil];
		[pasteBoard setString:result forType:NSStringPboardType];
	}
	
	TMWindow *window = [(TextmasterAppDelegate *)[NSApp delegate] window];
	[window setNeedsToPaste:_shouldPasteAfterwards];
	[window performClose:nil];
	
	_shouldPasteAfterwards = YES;
	[self setUserCanEditSampleText:NO];
}

- (void)macro:(id<TMMacro>)macro didFailWithErrorMessage:(NSString *)errorMessage {
	[self setShowActivityIndicator:NO];
	NSAlert *alert = [NSAlert alertWithMessageText:@"Error" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:errorMessage];
	[alert setAlertStyle:NSCriticalAlertStyle];
	[alert runModal];
}

// This gets called by a hidden NSButton that waits for Cmd+Return
- (IBAction)executeMacro:(id)sender {
//- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor {
	int row = [_tableView selectedRow];
	if(row<0)
		return;// YES;
	NSObject<TMMacro> *macro;
	if(_filteredMacros)
		macro = [_filteredMacros objectAtIndex:row];
	else
		macro = [_macros objectAtIndex:row];
	
	NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
	
	[macro setDelegate:self];
	[macro processText:[pasteBoard stringForType:NSStringPboardType]];
	
//	return YES;
}



#pragma mark -
#pragma mark Table view methods

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
	if(_filteredMacros)
		return [_filteredMacros count];
	return [_macros count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
	NSObject<TMMacro> *macro;
	if(_filteredMacros)
		macro = [_filteredMacros objectAtIndex:row];
	else
		macro = [_macros objectAtIndex:row];
	return [macro title];
}

-(void)tableViewSelectionDidChange:(NSNotification *)notification {
    int row = [_tableView selectedRow];
	
    if (row == -1) {
		[_titleText setStringValue:@"Nothing Selected"];
		[_authorText setStringValue:@"Please select a Textmaster macro."];
		[_descriptionText setString:@""];
		[_beforeText setString:@"(No sample text)"];
		[_afterText setString:@"(No sample text)"];
	}
	else {
		NSObject<TMMacro> *macro;
		if(_filteredMacros)
			macro = [_filteredMacros objectAtIndex:row];
		else
			macro = [_macros objectAtIndex:row];
		[_titleText setStringValue:[macro title]];
		[_authorText setStringValue:[macro author]];
		[_descriptionText setString:[macro description]];
		
		if([macro beforeText] && [macro afterText]) {
			[_beforeText setString:[macro beforeText]];
			[_afterText setString:[macro afterText]];
		}
		else {
			[_beforeText setString:@"(No sample text)"];
			[_afterText setString:@"(No sample text)"];
		}
	}
}

- (void)acceptFilenameDrag:(NSString *)str {
	[self installMacroAtPath:str];
}

@end
